/*c
 * Heap.cpp
 *
 *  Created on: 15-Nov-2009
 *
 */

#include "Heap.h"
#include "../BufferManager/BufferManager.h"
#include <iostream>
#include<malloc.h>

extern BufferManager *buffer;
extern int noOfBufferFrame;
extern Database *db;

#define checkStatus(status) if(status) \
		{\
	std:://cout<<"Error in Buffer Init";\
	return;\
		}

void Heap2::init(int dphfile)
{

}
Heap2::Heap2(int dphfile)
{
	if(dphfile == -1)
		createNewDB();
	else
		init(dphfile);
}

Status Heap2::createNewDB()
{
	Page *head;
	dbheader *header;
	//cout << "Now initialising Database\n";
	DEBUG_MSG_HEAP("Now initializing Database\n","",heap_debug);
	head = getNewPagePtr();

	header = (dbheader*)(head->pageData);

	Page *systable = createDPPage();

	Page *syscol = createDPPage();

	Page *sysind = createDPPage();

	writePage(systable->pageno,systable);
	writePage(syscol->pageno,syscol);
	writePage(sysind->pageno,sysind);
	//DEBUG_MSG("DB Initialized%s\n","");
	DEBUG_MSG_HEAP("Initialized\n","",heap_debug);
	header->systable.ptr = systable->pageno;
	header->syscols.ptr = syscol->pageno;
	header->sysindex.ptr = sysind->pageno;
	writePage(head->pageno,head);
	return ok;
}



Page* Heap2::createDPPage()
{
	Page *sys = getNewPagePtr();
	dpage *syspage = (dpage*)(sys->pageData);
	syspage->header.next.ptr = -1;
	syspage->header.maxfreespace = 0;
	for(unsigned int i=0;i<MAX_NOS_DES;i++)
	{
		syspage->des[i].page.ptr = -1;
		syspage->des[i].freespace = MAX_FREE_SPACE;
	}
	return sys;
}

Page* Heap2::createDBPage(long parent)
{
	Page *ret = getNewPagePtr();
	dbpage *newdbpage = (dbpage*)(ret->pageData);
	newdbpage->parent.ptr = parent;
	newdbpage->data = new unsigned char[MAX_FREE_SPACE];
	sde cfs;
	cfs.offset = 0;
	cfs.size = MAX_FREE_SPACE - DBHEADER_SIZE;
	*(int*)(ret->pageData + PAGE_SIZE - sizeof(int)) = 1;
	*(sde*)(ret->pageData + PAGE_SIZE - sizeof(sde) - sizeof(int)) = cfs;
	return ret;
}

Page* Heap2::getPagePtr(dp pageptr)
{
	int pageNum = pageptr.ptr;
	Page* page = (Page*) malloc(sizeof(Page));
	buffer->pinPage(pageNum,page);
	//cout << "Reading page number" << pageptr.ptr << endl;
	DEBUG_MSG_HEAP("Reading page number %d\n",(int)pageptr.ptr,heap_debug);
	return page;
}

Page* Heap2::getNewPagePtr()
{
	int retPageNum;
	Page* page = (Page*) malloc(sizeof(Page));
	page->pageData = (unsigned char*) malloc(sizeof(PAGE_SIZE));
	buffer->newPage(retPageNum,page);
	//cout << "Allocated page number " << retPageNum << endl;
	DEBUG_MSG_HEAP("Reading page number %d\n",retPageNum,heap_debug);
	return page;

}

void Heap2::writePage(long pageno,Page* page)
{
	page->dirty = 1;
	buffer->unpinPage(pageno,0);

}

void Heap2::unPinPage(long pageno)
{
	buffer->unpinPage(pageno,0);
}


Status Heap2::insertRecord(int file,unsigned char* recptr,int reclen,RID& outrid)
{
	Status status = error;
	dp filedp;
	filedp.ptr = file; // starting page of the table in which to insert data
	Page *target,*tabheadpage = getPagePtr(filedp);
	Page *newdppage = NULL;  // used when new page has to be created
	Page *prevdppage = NULL; // used to store previous page in the case of new page being created
	if(tabheadpage == NULL)
	{
		//cout<<"Heap2::insertRecord - Error - Invalid File";
		DEBUG_MSG_HEAP("Heap2::insertRecord - Error - Invalid File\n","",heap_debug);
		status = error;
		return status;
	}
	dpage *tabhead = (dpage*)(tabheadpage->pageData);
	Page *currtabheadpage = tabheadpage;
	dpage *currtabhead = tabhead;
	int deid;
	bool flag = false; // used for checking whether the de elements of dp page point to any db page or not

	while(true)						//not using maxfreespace
	{
		bool isfound = false; // flag use to find the de true if de with space is there
		for(unsigned int i=0;i<MAX_NOS_DES;i++)
		{
			DEBUG_MSG_HEAP("Traversing Db page number %d\n",currtabhead->des[i].page.ptr,heap_debug);
			if(currtabhead->des[i].freespace >= (reclen + (int)sizeof(sde)))
			{
				isfound = true;
				deid = i;
				if(currtabhead->des[i].page.ptr != -1) // if de found has already some of the slots filled
				{
					flag = true;
					target = getPagePtr(currtabhead->des[i].page);
					//cout << "Found space at page number "<< target->pageno << endl;
					DEBUG_MSG_HEAP("Found space at page number %d\n",target->pageno,heap_debug);
				}
				break;
			}
		}
		if(isfound) // if de with some slots empty is found break from while loop
			break;

		if(currtabhead->header.next.ptr != -1) // this is the case where we are going into next Dp page and checking for the space availability
		{
			DEBUG_MSG_HEAP("Going to next Dp now page number %d\n",currtabhead->header.next,heap_debug);
			currtabheadpage = getPagePtr(currtabhead->header.next);
			if(currtabheadpage == NULL)
			{
				//cout<<"Heap2::insertRecord - Error - Invalid File";
				DEBUG_MSG_HEAP("Heap2::insertRecord - Error - Invalid File\n","",heap_debug);
				status = error;
			}
			currtabhead = (dpage*)(currtabheadpage->pageData);
		}
		else // new Dp doesn't exist so we will create new dp page here
		{
			//TODO: create new DP page n fit em in DP chain
			DEBUG_MSG("Adding New DP Page%s\n","");
			//cout << "Creating new dp page" << endl;
			DEBUG_MSG_HEAP("Creating new dp page\n","",heap_debug);
			newdppage = createDPPage();
			dpage *newdp = (dpage*)(newdppage->pageData);
			currtabhead->header.next.ptr = newdppage->pageno;
			prevdppage = currtabheadpage;
			currtabheadpage = newdppage;
			currtabhead = newdp;
			//break;
		}

	}

	if(!flag) // after creating new dp isfound will be true but flag will be false so we have to create new db page in that case
	{
		//TODO; Get new page and write in it
		//DEBUG_MSG("Adding new DBPage%s","\n");
		//cout << "Creating new db page here" << endl;
		DEBUG_MSG_HEAP("Creating new db page\n","",heap_debug);
		target = createDBPage(currtabheadpage->pageno);

	}

	int nosslot = *(int*)(target->pageData + PAGE_SIZE  - sizeof(int));
	sde slotdir[nosslot];
	int offset = -1;
	int i;

	for(i=0;i<nosslot;i++)
	{
		slotdir[i] = *(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (i+1)*sizeof(sde));

		if(i != 0 && slotdir[i].offset < 0 && slotdir[i].size >= reclen) // condition when the slot is being deleted or slot or it is not slot1
		{
			offset = -(slotdir[i].offset);
			(*(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (i+1)*sizeof(sde))).offset = offset;
			(*(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (i+1)*sizeof(sde))).size = reclen;
			break;
		}
	}
	if(offset == -1) // creating slot here
	{
		//TODO: Get free location from cfs and update all values
		offset = slotdir[0].offset;
		(*(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (i+1)*sizeof(sde))).offset = slotdir[0].offset;
		(*(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (i+1)*sizeof(sde))).size = reclen;
		slotdir[0].offset += reclen;
		slotdir[0].size -= reclen;
		nosslot++;
		*(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - sizeof(sde)) = slotdir[0];
		*(int*)(target->pageData + PAGE_SIZE  - sizeof(int)) = nosslot;

	}

	for(int j=0;j<reclen;j++)
	{
		*(unsigned char*)(target->pageData + DBHEADER_SIZE + offset + j) = *(unsigned char*)(recptr + j); // inserting data here
	}

	outrid.pageno = target->pageno;
	outrid.slotno = i;
	currtabhead->des[deid].page.ptr = target->pageno;
	currtabhead->des[deid].freespace -= (reclen + sizeof(sde));
	if(prevdppage != NULL)
		writePage(prevdppage->pageno,prevdppage);
	writePage(currtabheadpage->pageno,currtabheadpage);
	writePage(target->pageno,target);

	status = ok;
	return status;

}

int Heap2::getRecCnt(int file)
{
	int count=0;
	dp filedp;
	filedp.ptr = file;
	Page *target,*tabheadpage = getPagePtr(filedp);
	if(tabheadpage == NULL)
	{
		//cout<<"Heap2::insertRecord - Error - Invalid File";
		DEBUG_MSG_HEAP("Heap2::insertRecord - Error - Invalid File\n","",heap_debug);
		return count;
	}
	dpage *tabhead = (dpage*)(tabheadpage->pageData);
	Page *currtabheadpage = tabheadpage;
	dpage *currtabhead = tabhead;
	while(true)
	{
		for(unsigned int i=0;i<MAX_NOS_DES;i++)
		{
			if(currtabhead->des[i].page.ptr == -1)
			{
				continue;
			}
			target = getPagePtr(currtabhead->des[i].page);
			int nosslot = *(int*)(target->pageData + PAGE_SIZE  - sizeof(int));
			sde slotdir[nosslot];
			for(int j=0;j<nosslot;j++)
			{
				slotdir[j] = *(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (j+1)*sizeof(sde));
				if(j != 0 && slotdir[j].offset >= 0)
				{
					count++;
				}
			}

		}
		if(currtabhead->header.next.ptr != -1)
		{

			currtabheadpage = getPagePtr(currtabhead->header.next);
			if(currtabheadpage == NULL)
			{
				//cout<<"Heap2::insertRecord - Error - Invalid File";
				DEBUG_MSG_HEAP("Heap2::insertRecord - Error - Invalid File\n","",heap_debug);
				return count;
			}
			currtabhead = (dpage*)(currtabheadpage->pageData);
		}
		else
		{
			return count;
		}

	}
	return count;
}

Status Heap2::getRecord(long file,const RID& rid,unsigned char *&recPtr, int& recLen)
{
	dp temp;
	temp.ptr = rid.pageno;
	Page *target = getPagePtr(temp);
	if(target == NULL)
	{
		//cout<<"Heap2::getRecord - Error - Invalid rid.pageno"<<endl;
		return error;
	}
	int nosslot = *(int*)(target->pageData + PAGE_SIZE  - sizeof(int));

	sde slot = *(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (rid.slotno+1)*sizeof(sde));
	if(rid.slotno >=nosslot || slot.offset < 0)
	{
		if(DEBUG) //cout<<"Heap2::getRecord - Error - Invalid rid.slotno"<<endl;
		return error;
	}
	//int offset = -1;
	recLen = slot.size;
	recPtr = new unsigned char[slot.size];
	for(int i=0;i<recLen;i++)
	{
		recPtr[i] = *(unsigned char*)(target->pageData +DBHEADER_SIZE + slot.offset + i);
	}

	return ok;
}

Status Heap2::deleteRecord(int file,const RID& rid)
{
	dp temp;
	temp.ptr = rid.pageno;
	Page *target = getPagePtr(temp);
	if(target == NULL)
	{
		//cout<<"Heap2::deleteRecord - Error - Invalid rid.pageno"<<endl;
		return error;
	}
	int nosslot = *(int*)(target->pageData + PAGE_SIZE  - sizeof(int));

	sde slot = *(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (rid.slotno+1)*sizeof(sde));
	if(rid.slotno >=nosslot || slot.offset < 0)
	{
		//cout<<"Heap2::deleteRecord - Error - Invalid rid.slotno"<<endl;
		return error;
	}
	(*(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (rid.slotno+1)*sizeof(sde))).offset = -slot.offset;
	writePage(target->pageno,target);
	return ok;
}

int Heap2::updateRecord(int file,RID& rid,unsigned char *recPtr, int reclen)
{
	dp temp;
	temp.ptr = rid.pageno;
	Page *target = getPagePtr(temp);
	sde cfs;
	if(target == NULL)
	{
		//cout<<"Heap2::updateRecord - Error - Invalid rid.pageno"<<endl;
		return error;
	}
	int nosslot = *(int*)(target->pageData + PAGE_SIZE  - sizeof(int));
	sde slot = *(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (rid.slotno+1)*sizeof(sde));
	if(rid.slotno >=nosslot || slot.offset < 0)
	{
		//cout<<"Heap2::updateRecord - Error - Invalid rid.slotno"<<endl;
		DEBUG_MSG_HEAP("Heap2::updateRecord - Error - Invalid rid.slotno\n","",heap_debug);
		return error;
	}
	int offset = -1;
	cfs = *(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - sizeof(sde));
	if(slot.size >= reclen)
	{
		slot.size = reclen;
		offset = slot.offset;
	}
	else
	{

		if(cfs.size >= reclen)
		{
			offset = cfs.offset;
			cfs.offset += reclen;
			cfs.size -= reclen;
			slot.offset = offset;
			slot.size = reclen;
//			*(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (rid.slotno+1)*sizeof(sde)) = slot;
		}
		else
		{
			//TODO: insert in other page and delete here and return
			RID ret;
			insertRecord(file,recPtr,reclen,ret);
			slot.offset = -slot.offset;
			rid = ret;
			*(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (rid.slotno+1)*sizeof(sde)) = slot;
			return 1;
		}
	}
	for(int j=0;j<reclen;j++)
	{
		*(unsigned char*)(target->pageData + DBHEADER_SIZE + offset + j) = *(unsigned char*)(recPtr + j);
	}
	*(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (rid.slotno+1)*sizeof(sde)) = slot;
	*(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - sizeof(sde)) = cfs;

	writePage(target->pageno,target);
	return 0;
}

int Heap2::openScan(int file,vector<record> &resultset)
{
	int count=0;
	dp filedp;
	record result;
	filedp.ptr = file;
	Page *target,*tabheadpage = getPagePtr(filedp);
	if(tabheadpage == NULL)
	{
		//cout<<"Heap2::insertRecord - Error - Invalid File";
		return count;
	}
	dpage *tabhead = (dpage*)(tabheadpage->pageData);
	Page *currtabheadpage = tabheadpage;
	dpage *currtabhead = tabhead;
	while(true)
	{
		for(unsigned int i=0;i<MAX_NOS_DES;i++)
		{
			if(currtabhead->des[i].page.ptr == -1)
			{
				//return count;
				continue;
			}
			target = getPagePtr(currtabhead->des[i].page);
			int nosslot = *(int*)(target->pageData + PAGE_SIZE  - sizeof(int));
			sde slotdir[nosslot];
			for(int j=0;j<nosslot;j++)
			{
				slotdir[j] = *(sde*)(target->pageData + PAGE_SIZE - sizeof(int) - (j+1)*sizeof(sde));
				if(j != 0 && slotdir[j].offset >= 0)
				{
					unsigned char *res = new unsigned char[slotdir[j].size];
					result.rec = new unsigned char[slotdir[j].size];
					for(int k=0;k<slotdir[j].size;k++)
					{
						result.rec[k] = *((unsigned char*)(target->pageData + DBHEADER_SIZE + slotdir[j].offset + k));
					}
					result.recLen = slotdir[j].size;
					result.rid.pageno = target->pageno;
					result.rid.slotno = j;
					resultset.push_back(result);
					count++;
				}
			}

		}
		if(currtabhead->header.next.ptr != -1)
		{

			currtabheadpage = getPagePtr(currtabhead->header.next);
			if(currtabheadpage == NULL)
			{
				//cout<<"Heap2::insertRecord - Error - Invalid File";
				return count;
			}
			currtabhead = (dpage*)(currtabheadpage->pageData);
		}
		else
		{
			return count;
		}

	}
	return count;
}

int Heap2::createHeapFile()
{
	Page *page = createDPPage();
	writePage(page->pageno,page);
	return page->pageno;
}

Status Heap2::deleteHeap(int file){

	Status status = ok;
	dp pageptr;
	pageptr.ptr=file;
	int firstDpPage = file;
	Page *currPage = getPagePtr(pageptr);

	if(currPage == NULL)
		status = error;
	dpage *currentDpPage = (dpage*)currPage->pageData;

	bool ismoreDp = true;

	while(ismoreDp){

		for(unsigned int i=0; i<MAX_NOS_DES; i++){

			if(currentDpPage->des[i].page.ptr!= -1){
				buffer->freePage(currentDpPage->des[i].page.ptr);
			}
		}

		file = currentDpPage->header.next.ptr;
		if(file != -1){
			pageptr.ptr = file;
			currPage = getPagePtr(pageptr);
			currentDpPage = (dpage*)currPage->pageData;
		}
		else
			ismoreDp=false;
	}
	return status;
};

void Heap2::test()
{
	RID rid;
	unsigned char temp[] = {'h','e','l','l','!'};
	for(long i=0;i<200000;i++)
		insertRecord(5,temp,5,rid);
	int len;
	unsigned char* temp2;
	getRecord(5,rid,temp2,len);
}

void Heap2::set_debug(){

	cout << "Enter 1 for Verbose and 0 for Non-Verbose mode";
	cin >> heap_debug;
}
